﻿/*
** Xin YUAN, 2023, BSD (2)
*/

////////////////////////////////////////////////////////////////////////////////
#ifndef __CTL_GDRAW_H__
#define __CTL_GDRAW_H__
////////////////////////////////////////////////////////////////////////////////

#ifndef __CTL_DEF_H__
	#error gdraw.h requires def.h to be included first.
#endif

////////////////////////////////////////////////////////////////////////////////
namespace CTL {
////////////////////////////////////////////////////////////////////////////////

//RasterHelper

class RasterHelper
{
public:
	//Bresenham

	// sx, sy : nullptr --- return the required length of array
	static int32_t CircleBresenham(int32_t radius, int32_t* sx, int32_t* sy) noexcept
	{
		int32_t n, t, i, j, m, ft;
		int32_t g, x, y;
		int32_t dinc, rinc;

		if ( radius <= 0 )
			return 0;

		// 1/8
		g = 3 - (2 * radius);
		x = 0;
		y = radius;
		dinc = 10 - 4 * radius;
		rinc = 6;

		n = 0;
		while ( x <= y ) {
			if ( sx && sy ) {
				sx[n] = x;
				sy[n] = y;
			}
			n ++;

			if ( g < 0 ) {
				g += rinc;
				dinc += 4;
			}
			else {
				g += dinc;
				dinc += 8;
				y --;
			} //end if
			rinc += 4;
			x ++;
		} //end while

		t = 8 * (n - 1);
		if ( x != y )
			t += 4;

		// all
		if ( sx && sy ) {
			if ( x != y )  ft = 2 * n - 1;
			else           ft = 2 * n - 2;

			// 0
			i = 1;
			sx[i * ft] = sy[0];
			sy[i * ft] = sx[0];
			i = 2;
			sx[i * ft] = sx[0];
			sy[i * ft] = -sy[0];
			i = 3;
			sx[i * ft] = -sy[0];
			sy[i * ft] = sx[0];

			// 45 degree
			if ( x == y ) {
				//n - 1
				i = 1;
				sx[i * ft + (n - 1)] = sx[n - 1];
				sy[i * ft + (n - 1)] = -sy[n - 1];
				i = 2;
				sx[i * ft + (n - 1)] = -sx[n - 1];
				sy[i * ft + (n - 1)] = -sy[n - 1];
				i = 3;
				sx[i * ft + (n - 1)] = -sx[n - 1];
				sy[i * ft + (n - 1)] = sy[n - 1];
			}
			m = n - 1;
			if ( x != y )  m ++;

			// rest
			for ( i = 1; i < m; i ++ ) {
				j = 1;
				sx[j * ft - i] = sy[i];
				sy[j * ft - i] = sx[i];
				sx[j * ft + i] = sy[i];
				sy[j * ft + i] = -sx[i];
				j = 2;
				sx[j * ft - i] = sx[i];
				sy[j * ft - i] = -sy[i];
				sx[j * ft + i] = -sx[i];
				sy[j * ft + i] = -sy[i];
				j = 3;
				sx[j * ft - i] = -sy[i];
				sy[j * ft - i] = -sx[i];
				sx[j * ft + i] = -sy[i];
				sy[j * ft + i] = sx[i];
				j = 4;
				sx[j * ft - i] = -sx[i];
				sy[j * ft - i] = sy[i];
			}
		} //end if

		return t;
	}

	// sx, sy : nullptr --- return the required length of array
	static int32_t Ellipse(int32_t a, int32_t b, int32_t* sx, int32_t* sy) noexcept
	{
		int32_t reverse, x, y, n, t, i, j, temp, ft;
		int64_t aa, bb, aa2, bb2, aa4, bb4;
		int64_t d, nt, xinc, yinc;

		if ( a <= 0 || b <= 0 )
			return 0;

		reverse = 0;
		if ( a < b ) {
			reverse = 1;
			temp = a;
			a = b;
			b = temp;
		}

		x = 0;
		y = b;

		aa = (int64_t)a * (int64_t)a;
		bb = (int64_t)b * (int64_t)b;

		//normal
		nt = b * (b - aa) * 2 + aa;
		//distance
		d = b * (b - aa) * 4 + aa;

		aa2 = aa * 2;
		bb2 = bb * 2;
		aa4 = aa2 * 2;
		bb4 = bb2 * 2;

		xinc = 3 * bb4;
		yinc = (2 - 2 * (int64_t)b) * aa4;

		n = 0;
		if ( sx && sy ) {
			sx[n] = x;
			sy[n] = y;
		}
		n ++;

		//top
		while ( nt < 0 ) {
			d += xinc;
			if ( d >= 0 ) {
				d += yinc;
				y --;
				nt += aa2;
				yinc += (aa4 * 2);
			}
			x ++;
			if ( sx && sy ) {
				sx[n] = x;
				sy[n] = y;
			}
			n ++;
			//update
			nt += bb2;
			xinc += (bb4 * 2);
		}
		//bottom
		d = bb * ((int64_t)x * ((int64_t)x + 1) * 4 + 1 - aa4)
			+ aa4 * ((int64_t)y - 1) * ((int64_t)y - 1);
		xinc = ((int64_t)x * 2 + 2) * bb4;
		yinc = (-(int64_t)y * 2 + 3) * aa4;
		while ( y > 0 ) {
			d += yinc;
			if ( d < 0 ) {
				d += xinc;
				x ++;
				xinc += (bb4 * 2);
			}
			y --;
			if ( sx && sy ) {
				sx[n] = x;
				sy[n] = y;
			}
			n ++;
			yinc += (aa4 * 2);
		}

		t = 4 * (n - 1);

		if ( sx && sy ) {
			ft = n - 1;

			if ( reverse ) {
				i = 1; j = n - 2;
				while ( i < j ) {
					temp = sy[j];
					sy[j] = sx[i];
					sx[i] = temp;
					temp = sx[j];
					sx[j] = sy[i];
					sy[i] = temp;
					i ++; j --;
				}
				temp = a;
				a = b;
				b = temp;
			}

			//0
			i = 0;
			sx[i * ft] = 0;
			sy[i * ft] = b;
			i = 1;
			sx[i * ft] = a;
			sy[i * ft] = 0;
			i = 2;
			sx[i * ft] = 0;
			sy[i * ft] = -b;
			i = 3;
			sx[i * ft] = -a;
			sy[i * ft] = 0;

			//rest
			for ( i = 1; i < n - 1; i ++ ) {
				//2
				j = 2;
				sx[j * ft - i] = sx[i];
				sy[j * ft - i] = -sy[i];
				sx[j * ft + i] = -sx[i];
				sy[j * ft + i] = -sy[i];
				//4
				j = 4;
				sx[j * ft - i] = -sx[i];
				sy[j * ft - i] = sy[i];
			}
		}

		return t;
	}

	typedef void (*RasterCallback)(int32_t x, int32_t y, void* p);  //noexcept

	// func : no throw
	// sx, sy : nullptr --- return the required length of array
	static int32_t LineBresenham(int32_t x1, int32_t y1, int32_t x2, int32_t y2,
					RasterCallback func, void* p,
					int32_t* sx, int32_t* sy) noexcept
	{
		int32_t  n, i;
		int32_t  x, y;
		int32_t  f, intercharge;
		int32_t  s1, s2;
		int32_t  dx, dy, temp;

		x = x1;
		y = y1;
		dx = NumberTraits<int32_t>::Abs(x2 - x1);
		dy = NumberTraits<int32_t>::Abs(y2 - y1);
		if ( x2 - x1 >= 0 )
			s1 = 1;
		else
			s1 = -1;
		if ( y2 - y1 >= 0 )
			s2 = 1;
		else
			s2 = -1;
		if ( dy > dx ) {
			temp = dx;
			dx = dy;
			dy = temp;
			intercharge = 1;
		}
		else {
			intercharge = 0;
		} //end if
		f = 2 * dy - dx;

		n = 0;
		for ( i = 0; i <= dx; i ++ ) {
			if ( sx && sy ) {
				sx[n] = x;
				sy[n] = y;
			} //end if
			if ( func != NULL )
				func(x, y, p);  //no throw
			n ++;
			if ( f >= 0 ) {
				if ( intercharge == 1 )
					x += s1;
				else
					y += s2;
				f = f - 2 * dx;
			} //end if
			if ( intercharge == 1 )
				y += s2;
			else
				x += s1;
			f = f + 2 * dy;
		} //end for

		return n;
	}
};

////////////////////////////////////////////////////////////////////////////////
}
////////////////////////////////////////////////////////////////////////////////
#endif
////////////////////////////////////////////////////////////////////////////////
